home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 10
/
AACD 10.iso
/
AACD
/
Online
/
SpeakFreely
/
src
/
lwld.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-05-18
|
50KB
|
1,950 lines
/*
Speak Freely for Unix
Look Who's Listening Server
*/
#include "speakfree.h"
#include "version.h"
#ifdef THREADS
#include <pthread.h>
#define Lock(x) pthread_mutex_lock(&(x))
#define Unlock(x) pthread_mutex_unlock(&(x))
#define LockConn() Lock(connLock)
#define UnlockConn() Unlock(connLock)
#else
#define LockConn()
#define UnlockConn()
#endif
#define TockTock 120 /* Timeout check frequency, seconds */
#define TimeoutTime 15 * 60 /* No-response timeout interval, seconds */
#define ServiceThreadTimeout 60 /* Service thread timeout interval, seconds */
#define MaxReplyPacket 512 /* Maximum length of reply data */
static int sock; /* Our socket */
static char *prog; /* Program name */
static int lwlport = Internet_Port + 2; /* Look Who's Listening port */
static int debugging = FALSE; /* Debug mode enabled */
static int verbose = FALSE; /* Show connections/disconnects */
static int prolix = FALSE; /* Extremely verbose (show queries) */
#ifdef HEXDUMP
static int hexdump = FALSE; /* Dump received packets in hex ? */
#endif
static char *htmlFile = NULL; /* HTML file name base */
static char *htmlPrivateFile = NULL; /* HTML private directory file name base */
static int htmlTime = 1 * 60; /* HTML file update time */
static time_t htmlLast = 0; /* HTML last update time */
static int htmlChange = TRUE; /* Change since last HTML update ? */
static int htmlRefresh = 0; /* HTML client-pull refresh interval */
#define HTML_REFRESH "<meta http-equiv=\"Refresh\" content=\"%d\">\n"
static char *message = NULL; /* Server information message */
static int messagel; /* Length of server information message */
#ifdef THREADS
#define ForwarderSleep 30 /* How often to update forwarded hosts */
#define ForwarderMaxQ 15 /* Maximum items in forward queue */
struct lwl_queueitem {
struct lwl_queueitem *next; /* Next item in LWL queue */
int q_rll; /* Length of message */
char q_pkt[2]; /* Message body */
};
struct forwarder {
struct forwarder *next; /* Next forwarder in chain */
int status; /* Site status (0 = normal) */
char *sitename; /* Site name specification */
struct in_addr site; /* Internet address of target host */
long port; /* Destination port on that host */
pthread_t thread; /* ID of forwarder thread */
int queuelen; /* Length of queue for forwarder */
struct lwl_queueitem *head, *tail;/* Queue of messages to be forwarded */
};
static pthread_attr_t detached; /* Attributes for our detached threads */
static pthread_mutex_t fwdlistLock = PTHREAD_MUTEX_INITIALIZER;
static struct forwarder *fwdlist = NULL; /* List of forwarding destinations */
#define lwl_nsites 1 /* Threaded version driven from list */
struct ballOstring {
struct ballOstring *next, *prev; /* Forward and back queue links */
pthread_t thread; /* Service thread ID */
time_t startTime; /* When did thread start ? */
struct in_addr site; /* Internet address of requesting site */
struct service_arg *sa; /* Service thread argument */
};
static struct ballOstring bshead; /* Ball of string queue head */
static pthread_mutex_t bsLock = PTHREAD_MUTEX_INITIALIZER;
#else
static struct sockaddr_in lookhost; /* Look who's listening host, if any */
static struct in_addr lwl_sites[LWL_MAX_SITES]; /* LWL site addresses */
static long lwl_ports[LWL_MAX_SITES]; /* Ports for LWL hosts */
static int lwl_nsites = 0; /* Number of LWL sites published on */
#endif
struct lwl {
struct lwl *next; /* Next connection */
time_t ltime; /* Time of last update */
unsigned long ssrc; /* Session source descriptor */
long naddr; /* Internet address */
short port; /* Port address */
char *cname; /* Canonical name */
char *name; /* User name */
char *email; /* Electronic mail address */
char *phone; /* Telephone number */
char *loc; /* Geographic location */
char *tool; /* Application name */
};
#ifdef THREADS
/* Connection chain critical section */
static pthread_mutex_t connLock = PTHREAD_MUTEX_INITIALIZER;
#endif
static struct lwl *conn = NULL; /* Chain of current connections */
/* ETIME -- Edit time and date for log messages. */
static char *etime(gmt)
int gmt;
{
struct tm *t;
time_t clock;
static char s[20];
time(&clock);
if (gmt) {
t = gmtime(&clock);
} else {
t = localtime(&clock);
}
sprintf(s, "%d-%02d-%02d %02d:%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min);
return s;
}
/* ESTIME -- Edit short time. */
static char *estime()
{
struct tm *t;
time_t clock;
static char s[20];
time(&clock);
t = localtime(&clock);
sprintf(s, "%02d:%02d", t->tm_hour, t->tm_min);
return s;
}
/* DUPSDESITEM -- Make a copy of an SDES item and advance the pointer
past it. */
static char *dupSdesItem(cp)
char **cp;
{
char *ip = *cp, *bp;
int l = ip[1] & 0xFF;
bp = malloc(l + 1);
if (bp != NULL) {
bcopy(ip + 2, bp, l);
bp[l] = 0;
}
*cp = ip + l + 2;
return bp;
}
/* DESTROYLWL -- Release storage associated with an LWL list item. */
static void destroyLwl(lw)
struct lwl *lw;
{
if (lw->cname != NULL) {
free(lw->cname);
}
if (lw->name != NULL) {
free(lw->name);
}
if (lw->email != NULL) {
free(lw->email);
}
if (lw->phone != NULL) {
free(lw->phone);
}
if (lw->loc != NULL) {
free(lw->loc);
}
if (lw->tool != NULL) {
free(lw->tool);
}
free(lw);
}
/* DUMPLWL -- Dump an LWL on the specified stream. */
static void dumpLwl(fo, lw)
FILE *fo;
struct lwl *lw;
{
struct sockaddr_in s;
s.sin_addr.s_addr = lw->naddr;
fprintf(fo, "SSRC = %lX IP = %s Port %d %s", lw->ssrc, inet_ntoa(s.sin_addr),
lw->port, ctime(&(lw->ltime)));
if (lw->cname != NULL) {
fprintf(fo, " CNAME = %s\n", lw->cname);
}
if (lw->name != NULL) {
fprintf(fo, " NAME = %s\n", lw->name);
}
if (lw->email != NULL) {
fprintf(fo, " EMAIL = %s\n", lw->email);
}
if (lw->phone != NULL) {
fprintf(fo, " PHONE = %s\n", lw->phone);
}
if (lw->loc != NULL) {
fprintf(fo, " LOC = %s\n", lw->loc);
}
if (lw->tool != NULL) {
fprintf(fo, " TOOL = %s\n", lw->tool);
}
}
#ifdef NEEDED
/* DUMPLWLCHAIN -- Dump all current connections. */
static void dumpLwlChain(fo)
FILE *fo;
{
struct lwl *lw;
LockConn();
lw = conn;
fprintf(fo, "\n========== %s ==========\n", etime(FALSE));
while (lw != NULL) {
fprintf(fo, "\n");
dumpLwl(fo, lw);
lw = lw->next;
}
UnlockConn();
}
#endif
/* LOGLWL -- Generate log entry for LWL event. */
static void logLwl(lw, event)
struct lwl *lw;
char *event;
{
char ipport[40], deef[256];
struct sockaddr_in u;
u.sin_addr.s_addr = lw->naddr;
sprintf(ipport, "%s:%d", inet_ntoa(u.sin_addr), lw->port);
sprintf(deef, "%s %s", ipport, lw->email ? lw->email : lw->cname);
if (lw->name) {
sprintf(deef + strlen(deef), " (%s)", lw->name);
}
printf("%s %s%s\n", estime(), event, deef);
}
/* GARDOL -- Validity-check an RTCP packet to make sure we don't
crash if some bozo sends us garbage. */
static int gardol(p, len)
unsigned char *p;
int len;
{
unsigned char *end;
if ((((p[0] >> 6) & 3) != RTP_VERSION) || /* Version incorrect ? */
((p[0] & 0x20) != 0) || /* Padding in first packet ? */
((p[1] != RTCP_SR) && (p[1] != RTCP_RR) &&
(p[1] != RTCP_SDES) && (p[1] != RTCP_BYE) &&
(p[1] != RTCP_APP))) { /* Item type valid ? */
return FALSE;
}
end = p + len;
do {
/* Advance to next subpacket */
p += (ntohs(*((short *) (p + 2))) + 1) * 4;
} while (p < end && (((p[0] >> 6) & 3) == RTP_VERSION));
return p == end;
}
/* MAKEHTML -- Create an HTML file showing current connections. If
"private" is set, exact-match names are included
in the HTML file; this allows creation of a non-exported
active site file for the system manager's use. */
static void makeHTML(fname, private)
char *fname;
int private;
{
FILE *of;
char f[132], fn[132];
strcpy(f, fname);
strcat(f, ".new");
of = fopen(f, "w");
if (of != NULL) {
struct lwl *lw;
#define P(x) fprintf(of, x)
LockConn();
lw = conn;
P("<html>\n<head>\n");
if (htmlRefresh > 0) {
fprintf(of, HTML_REFRESH, htmlRefresh);
}
P("<title>\nSpeak Freely: Active Sites\n");
P("</title>\n</head>\n\n<body>\n<center>\n<h1>");
P("Speak Freely: Active Sites</h1>\n<h2>");
fprintf(of, "As of %s UTC</h2>\n</center>\n<p>\n", etime(TRUE));
if (lw == NULL) {
P("<h2>No sites active.</h2>\n");
} else {
int i = 0;
while (lw != NULL) {
if (private || ((lw->email == NULL || lw->email[0] != '*') &&
(lw->cname[0] != '*'))) {
i++;
}
lw = lw->next;
}
lw = conn;
if (i == 0) {
/* Fib about number of sites active if all active sites
don't want a directory listing. */
P("<h2>No sites active.</h2>\n");
} else {
fprintf(of, "<h2>%d site%s active.</h2>\n",
i, i == 1 ? "" : "s");
P("<pre>\n");
while (lw != NULL) {
if (private || ((lw->email == NULL || lw->email[0] != '*') &&
(lw->cname[0] != '*'))) {
char ipport[40];
struct tm *lt;
struct sockaddr_in u;
char s[384];
lt = gmtime(&lw->ltime);
u.sin_addr.s_addr = lw->naddr;
sprintf(ipport, "%s:%d", inet_ntoa(u.sin_addr), lw->port);
sprintf(s, "\n%-24s %-48s %02d:%02d\n", ipport, lw->cname,
lt->tm_hour, lt->tm_min);
outHTML(of, s);
if (lw->name != NULL) {
sprintf(s, "%25s%s\n", "", lw->name);
outHTML(of, s);
}
if (lw->loc != NULL) {
sprintf(s, "%25s%s\n", "", lw->loc);
outHTML(of, s);
}
if (lw->phone != NULL) {
sprintf(s, "%25sPhone: %s\n", "", lw->phone);
outHTML(of, s);
}
if (lw->email != NULL) {
sprintf(s, "%25sE-mail: %s\n", "", lw->email);
outHTML(of, s);
}
}
lw = lw->next;
}
P("</pre>\n");
}
}
UnlockConn();
P("</body>\n</html>\n");
fclose(of);
strcpy(fn, fname);
strcat(fn, ".html");
rename(f, fn);
if (debugging) {
fprintf(stderr, "%s: updated %s\n", prog, fn);
}
}
}
/* UPDHTML -- Update HTML if necessary. */
static void updHTML()
{
time_t now;
if (((htmlFile != NULL) || (htmlPrivateFile != NULL)) && htmlChange &&
((htmlTime <= 0) || (((now = time(NULL)) - htmlLast) > htmlTime))) {
htmlLast = now;
htmlChange = FALSE;
if (htmlFile != NULL) {
makeHTML(htmlFile, FALSE);
}
if (htmlPrivateFile != NULL) {
makeHTML(htmlPrivateFile, TRUE);
}
}
}
/* CHANGED -- Indicate a change which may require updating
the HTML file. */
static void changed()
{
htmlChange = TRUE;
#ifndef THREADS
updHTML();
#endif
}
/* LCASE -- Convert a string to lower case. */
static void lcase(s)
char *s;
{
while (*s) {
if (isupper(*s)) {
*s = tolower(*s);
}
s++;
}
}
#ifdef THREADS
/* FORWARDER_THREAD -- Thread which manages forwarding to a given
destination. */
static void *forwarder_thread(arg)
void *arg;
{
struct forwarder *f = (struct forwarder *) arg;
struct hostent *h;
long iadr;
#ifdef DBTHREAD
fprintf(stderr, "Started forwarder thread for %s\n", f->sitename);
#endif
if (isdigit(f->sitename[0]) && (iadr = inet_addr(f->sitename)) != -1) {
bcopy((char *) &iadr, (char *) (&(f->site)),
sizeof iadr);
f->status = 0;
} else {
h = gethostbyname(f->sitename);
if (h != NULL) {
bcopy((char *) (h->h_addr),
(char *) (&(f->site)),
sizeof(unsigned long));
f->status = 0;
} else {
fprintf(stderr, "%s: warning, forward destination %s unknown.\n",
prog, f->sitename);
f->status = -2;
return NULL;
}
}
if (debugging && f->status == 0) {
fprintf(stderr, "%s: forwarding to LWL server %s: %s.\n", prog,
inet_ntoa(f->site), f->sitename);
}
while (f->status == 0) {
struct lwl_queueitem *q;
int sock = -1;
while ((q = f->head) != NULL) {
int cstat;
struct sockaddr_in lookhost;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening forwarding socket");
break;
}
lookhost.sin_port = htons(f->port);
bcopy((char *) (&(f->site)), (char *) &lookhost.sin_addr.s_addr,
sizeof lookhost.sin_addr.s_addr);
errno = 0;
do {
cstat = connect(sock, (struct sockaddr *) &(lookhost), sizeof lookhost);
if (cstat >= 0) {
break;
}
} while (errno == EINTR);
if (cstat >= 0) {
if (send(sock, q->q_pkt, q->q_rll, 0) < 0) {
perror("forwarding look who's listening source ID message");
break;
} else {
Lock(fwdlistLock);
f->head = q->next;
if (f->head == NULL) {
f->tail = NULL;
}
free(q);
f->queuelen--;
if (f->queuelen < 0) {
fprintf(stderr, "%s: forward queue underflow to %s.\n",
prog, f->sitename);
f->queuelen = 0;
}
Unlock(fwdlistLock);
if (debugging) {
fprintf(stderr, "%s: forwarded packet to server %s. Queue length now %d.\n",
prog, f->sitename, f->queuelen);
}
}
} else {
/* As LWL servers go up and down, failures to connect
to forwarding destinations are not uncommon. Further,
while a server is down, we get here on every packet
we attempt to forward. To avoid blithering all
over the log, we only note such failure if in
prolix or debugging mode. */
if (prolix || debugging) {
perror("connecting forwarding socket");
}
break;
}
close(sock);
sock = -1;
}
/* If we bailed out of the above loop due to an error in
forwarding the packet, make sure the socket is closed. */
if (sock >= 0) {
close(sock);
sock = -1;
}
sleep(ForwarderSleep);
}
#ifdef DBTHREAD
fprintf(stderr, "Exited forwarder thread for %s\n", f->sitename);
#endif
f->status = -1; /* Forwarder terminated */
return NULL;
}
#endif
/* FORWARDLIST -- Parse a list of servers to forward packets to. */
static void forwardList(cp)
char *cp;
{
#ifdef THREADS
struct forwarder *f;
char *ep, *np;
while (TRUE) {
while (*cp != 0 && isspace(*cp)) {
cp++;
}
if (*cp == 0) {
break;
}
if ((np = strchr(cp, ',')) != NULL) {
*np++ = 0;
}
f = (struct forwarder *) malloc(sizeof(struct forwarder));
if (f != NULL) {
bzero(f, sizeof(struct forwarder));
f->status = 1;
f->sitename = cp;
f->port = Internet_Port + 2;
if ((ep = strchr(cp, ':')) != NULL) {
*ep = 0;
f->port = atoi(ep + 1);
}
f->next = fwdlist;
fwdlist = f;
if (pthread_create(&(f->thread), &detached, forwarder_thread, f) != 0) {
fprintf(stderr, "%s: Cannot launch forwarder thread for %s: %s",
prog, cp, strerror(errno));
free(f);
}
} else {
fprintf(stderr, "%s: out of memory allocating forwarder for %s\n",
prog, cp);
break;
}
if (np == NULL) {
break;
}
cp = np;
}
#else
struct hostent *h;
char *ep, *np;
long iadr;
int n;
lookhost.sin_family = AF_INET;
while (lwl_nsites < LWL_MAX_SITES) {
n = lwl_nsites;
while (*cp != 0 && isspace(*cp)) {
cp++;
}
if (*cp == 0) {
break;
}
if ((np = strchr(cp, ',')) != NULL) {
*np++ = 0;
}
lwl_ports[lwl_nsites] = Internet_Port + 2;
if ((ep = strchr(cp, ':')) != NULL) {
*ep = 0;
lwl_ports[lwl_nsites] = atoi(ep + 1);
}
if (isdigit(*cp) && (iadr = inet_addr(cp)) != -1) {
bcopy((char *) &iadr, (char *) (&lwl_sites[lwl_nsites]),
sizeof iadr);
lwl_nsites++;
} else {
h = gethostbyname(cp);
if (h != NULL) {
bcopy((char *) (h->h_addr),
(char *) (&lwl_sites[lwl_nsites]),
sizeof(unsigned long));
lwl_nsites++;
} else {
fprintf(stderr, "%s: warning, forward destination %s unknown.\n",
prog, cp);
}
}
if (debugging && lwl_nsites > n) {
fprintf(stderr, "%s: forwarding to LWL server %s: %s.\n", prog,
inet_ntoa(lwl_sites[n]), cp);
}
if (np == NULL) {
break;
}
cp = np;
}
#endif
}
/* FORWARDLWLMESSAGE -- If forwarding is enabled, pass this message
on to hosts in our forward list, changing the
packet protocol header to indicate the packet
was forwarded and appending the IP address
of the originating host to the end of the packet. */
static void forwardLwlMessage(zp, rll, addr)
char *zp; /* Packet address */
int rll; /* Total packet length (starting at zp) */
struct in_addr addr; /* IP address of originator of this packet */
{
#ifndef THREADS
int i, sock;
#endif
zp[0] |= 0x40; /* Set forwarded packet flag bit */
bcopy(&addr, zp + rll, sizeof(struct in_addr));
rll += sizeof(struct in_addr);
#ifdef THREADS
{
struct forwarder *f = fwdlist;
while (f != NULL) {
if (f->status == 0) {
if (f->queuelen > ForwarderMaxQ) {
fprintf(stderr, "%s: %s queue too long; forward message discarded.\n",
prog, f->sitename);
} else {
struct lwl_queueitem *q = (struct lwl_queueitem *) malloc(
sizeof(struct lwl_queueitem) + rll);
if (q != NULL) {
q->next = NULL;
q->q_rll = rll;
bcopy(zp, q->q_pkt, rll);
Lock(fwdlistLock);
f->queuelen++;
if (f->head == NULL) {
f->head = f->tail = q;
} else {
f->tail->next = q;
f->tail = q;
}
Unlock(fwdlistLock);
}
}
}
f = f->next;
}
}
#else
for (i = 0; i < lwl_nsites; i++) {
if (lwl_ports[i] >= 0) {
int cstat;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening forwarding socket");
continue;
}
lookhost.sin_port = htons(lwl_ports[i]);
bcopy((char *) (&lwl_sites[i]), (char *) &lookhost.sin_addr.s_addr,
sizeof lookhost.sin_addr.s_addr);
errno = 0;
do {
cstat = connect(sock, (struct sockaddr *) &(lookhost), sizeof lookhost);
if (cstat >= 0) {
break;
}
} while (errno == EINTR);
if (cstat >= 0) {
if (send(sock, zp, rll, 0) < 0) {
perror("forwarding look who's listening source ID message");
} else {
if (debugging) {
fprintf(stderr, "%s: forwarded packet to server %s.\n",
prog, inet_ntoa(lookhost.sin_addr));
}
}
} else {
/* As LWL servers go up and down, failures to connect
to forwarding destinations are not uncommon. Further,
while a server is down, we get here on every packet
we attempt to forward. To avoid blithering all
over the log, we only note such failure if in
prolix or debugging mode. */
if (prolix || debugging) {
perror("connecting forwarding socket");
}
}
close(sock);
}
}
#endif
}
/* QUERYMATCH -- Determing if query matches a given item. */
static int queryMatch(q, l)
char *q;
struct lwl *l;
{
char ts[1024];
int exact = FALSE;
char *s;
s = ts;
/* We give preference to an E-mail address, if given, to a
canonical address since it's the user's best known published
identity on the net. This, of course, runs the risk of
spoofing, but since anybody can send us an RTCP packet with
whatever cname they like, there's no added security in insisting
on using it. */
strcpy(s, (l->email == NULL) ? l->cname : l->email);
lcase(s);
if (*q == '*') {
exact = TRUE;
q++;
}
if (*s == '*') {
exact = TRUE;
s++;
}
/* Even if we're using the E-mail name, allow an asterisk on the
canonical name to require an exact match. */
if (l->cname[0] == '*') {
exact = TRUE;
}
if (exact) {
return strcmp(s, q) == 0;
}
if (strstr(s, q)) {
return TRUE;
}
if (l->name != NULL) {
strcpy(s, l->name);
lcase(s);
if (strstr(s, q)) {
return TRUE;
}
}
return FALSE;
}
/* RELEASE -- Check for connections who haven't send us an update
once in the timeout interval and close them. They
probably went away without having the courtesy to
say good-bye. */
static void release()
{
struct lwl *lw, *llr = NULL, *lf;
time_t now = time(NULL);
LockConn();
lw = conn;
while (lw != NULL) {
if ((now - lw->ltime) > TimeoutTime) {
lf = lw;
if (llr == NULL) {
conn = lw->next;
lw = conn;
} else {
llr->next = lw->next;
lw = lw->next;
}
/*
fprintf(stderr, "\nTiming out:\n");
dumpLwl(stderr, lf);
*/
if (verbose) {
logLwl(lf, "Timeout: ");
}
htmlChange = TRUE;
destroyLwl(lf);
} else {
llr = lw;
lw = lw->next;
}
}
UnlockConn();
#ifndef THREADS
updHTML(); /* Update HTML if something's changed recently */
signal(SIGALRM, release); /* Reset signal to handle timeout (Sys V) */
alarm(TockTock); /* Reset the timer */
#endif
}
/* PLUMBER -- Catch SIGPIPE signal which occurs when remote user
disconnects before we try to send the reply. */
static void plumber()
{
#ifdef DBTHREAD
fprintf(stderr, "Caught SIGPIPE--continuing.\n");
#endif
signal(SIGPIPE, plumber); /* Reset signal just in case */
}
/* EXITING -- Release socket in case of termination. */
static void exiting()
{
#ifdef DBTHREAD
fprintf(stderr, "Exiting.\n");
#endif
shutdown(sock, 2);
close(sock);
exit(0);
}
/* PROG_NAME -- Extract program name from argv[0]. */
static char *prog_name(arg)
char *arg;
{
char *cp = strrchr(arg, '/');
return (cp != NULL) ? cp + 1 : arg;
}
/* USAGE -- Print how-to-call information. */
static void usage()
{
V fprintf(stderr, "%s -- Speak Freely: Look Who's Listening Server.\n", prog);
#ifdef THREADS
V fprintf(stderr, " (Multi-threaded version)\n");
#endif
V fprintf(stderr, " %s.\n", Relno);
V fprintf(stderr, "\n");
V fprintf(stderr, "Usage: %s [options]\n", prog);
V fprintf(stderr, "Options:\n");
V fprintf(stderr, " -D Debugging output on stderr\n");
V fprintf(stderr, " -Fserv,... Forward to listed servers\n");
V fprintf(stderr, " -Hpath Write HTML status on base path\n");
V fprintf(stderr, " -Insec Interval between HTML updates\n");
V fprintf(stderr, " -Mfile Load server message from file\n");
V fprintf(stderr, " -Pport Listen on given port\n");
V fprintf(stderr, " -Rnsec Client-pull HTML refresh every nsec seconds\n");
V fprintf(stderr, " -U Print this message\n");
V fprintf(stderr, " -V[V] List connections and disconnects [-VV: all packets]\n");
#ifdef HEXDUMP
V fprintf(stderr, " -X Dump packets in hex\n");
#endif
V fprintf(stderr, " -Zpath Write HTML private directory on base path\n");
V fprintf(stderr, "\n");
V fprintf(stderr, "by John Walker\n");
V fprintf(stderr, " http://www.fourmilab.ch/\n");
}
/* ADD_SDES_ITEM -- Include an SDES item in a reply packet,
pruning illegal characters and content,
checking for packet overflow, and updating
the packet pointer. */
#define addSDES(item, text) add_sdes_item(item, text, &ap)
static void add_sdes_item(item, text, app)
int item;
char *text;
char **app;
{
int l, state;
char prune[258];
char *ap = *app, *hp, *op = prune;
/* Copy the text to the "prune" buffer using a simple
state machine parser, filtering extraneous
information. */
state = 0;
while (*text) {
unsigned char ch = (unsigned char) *text++;
if (item == RTCP_SDES_PRIV && op == prune) {
/* Allow PRIV item binary prefix field to pass. */
*op++ = (char) ch;
continue;
}
/* Discard non-graphic characters (ISO 8859 standard). */
if (!isspace(ch) && (ch < ' ' || (ch > '~' && ch < 161))) {
continue;
}
switch (state) {
case 0: /* Skipping initial white space */
if (!isspace(ch)) {
state = 1;
} else {
break;
}
/* Note fall-through */
case 1: /* Transcribing to output */
s1: if (ch == '<') {
if ((hp = strchr(text, '>')) != NULL) {
text = hp + 1;
if (op > prune && isspace(op[-1])) {
state = 2;
}
continue;
}
}
if (isspace(ch)) {
ch = ' '; /* Convert all white space into blanks */
state = 2;
}
*op++ = (char) ch;
break;
case 2: /* Collapsing multiple white space */
if (!isspace(ch)) {
state = 1;
goto s1;
}
break;
}
}
*op = 0;
/* Drop trailing blanks. */
while (strlen(prune) > 0 && isspace(op[-1])) {
*(--op) = 0;
}
l = strlen(prune);
if (l == 0) {
return;
} else if (l > 48) {
int n;
/* String is too long. Try to trim it at a word boundary,
but if that fails just brutally lop it off. */
for (n = 47; n > 31; n--) {
if (isspace(prune[n])) {
prune[n] = 0;
n = 0;
break;
}
}
if (n > 0) {
prune[47] = 0;
}
}
*ap++ = item;
*ap++ = l = strlen(prune);
bcopy(prune, ap, l);
ap += l;
*app = ap;
}
/* SERVICEPACKET -- Process a packet from a connection. */
static void servicePacket(csock, from)
int csock;
struct sockaddr_in from; /* Sending host address */
{
int rll, forward, pvalid;
char zp[1024];
rtcp_t *rp;
short srp;
struct lwl *lw; /* Parsed packet */
char *p, *pend;
struct sockaddr_in fwdh;
forward = FALSE;
rll = recv(csock, zp, sizeof zp, 0);
#ifdef HEXDUMP
if (hexdump) {
fprintf(stderr, "%s: %d bytes read from socket.\n", prog, rll);
xd(stderr, zp, rll, TRUE);
}
#endif
/* If packet was forwarded from another server, extract the
embedded original source address and make the packet
look like it came directly from the sender. */
if (rll > 4 && (zp[0] & 0xC0) == 0xC0) {
forward = TRUE;
zp[0] &= ~0x40;
bcopy(zp + (rll - 4), &fwdh.sin_addr, 4);
rll -= 4;
if (debugging) {
fprintf(stderr, "%s: received forwarded packet from %s.\n",
prog, inet_ntoa(from.sin_addr));
}
from.sin_addr = fwdh.sin_addr ;
}
/* Some version of Speak Freely for Windows don't always
pad request packets to a multiple of four bytes. If
this is the case, insert the pad ourselves. */
while (((zp[1] & 0xFF) == RTCP_APP) && ((rll & 3) != 0)) {
zp[rll++] = 0;
}
/* Validate this packet. If it fails the validity check,
ignore it do it can't crash the code below. */
if (!gardol((unsigned char *) zp, rll)) {
if (debugging) {
fprintf(stderr, "%s: discarded invalid RTCP packet from %s.\n",
prog, inet_ntoa(from.sin_addr));
}
close(csock);
return;
}
/* Walk through the individual items in a possibly composite
packet until we locate an item we're interested in. This
allows us to accept packets that comply with the RTP standard
that all RTCP packets begin with an SR or RR. */
p = zp;
pend = p + rll;
pvalid = FALSE;
while ((p < pend) && (p[0] >> 6 & 3) == RTP_VERSION) {
int pt = p[1] & 0xFF;
if (pt == RTCP_SDES || pt == RTCP_BYE || pt == RTCP_APP) {
pvalid = TRUE;
break;
}
/* If not of interest to us, skip to next subpacket. */
p += (ntohs(*((short *) (p + 2))) + 1) * 4;
}
if (!pvalid) {
if (debugging) {
fprintf(stderr, "%s: discarded RTCP packet with unknown payload from %s.\n",
prog, inet_ntoa(from.sin_addr));
}
close(csock);
return;
}
/* Examine the packet to see what kind of request it is.
Note that since all the packets we're interested in can
be parsed without knowing their actual length in bytes, we
can ignore the possible presence of padding. */
rp = (rtcp_t *) p;
srp = ntohs(*((short *) p));
if (
#ifdef RationalWorld
rp->common.version == RTP_VERSION && /* Version ID correct */
rp->common.count == 1
#else
(((srp >> 14) & 3) == RTP_VERSION) &&
(((srp >> 8) & 0x1F) == 1)
#endif
) {
switch (
#ifdef RationalWorld
rp->common.pt
#else
srp & 0xFF
#endif
) {
/* SDES packet. This is a notification by a listening
that it's just started or is still
listening. If the host was previously
active we replace the old parameters
with the new in case something has
changed. The timeout is reset. Identity
of host is by canonical name, not SSRC,
since the host may have restarted with
a new SSRC without having sent us a BYE. */
case RTCP_SDES:
/* Parse the fields out of the SDES packet. */
{
char *cp = (char *) rp->r.sdes.item,
*lp = cp + (ntohs(rp->common.length) * 4);
struct lwl *lr, *llr;
lw = (struct lwl *) malloc(sizeof(struct lwl));
if (lw != NULL) {
bzero((char *) lw, sizeof(struct lwl));
lw->ssrc = rp->r.sdes.src;
lw->naddr = from.sin_addr.s_addr;
lw->port = from.sin_port;
lw->ltime = time(NULL);
if (prolix) {
printf("%s: %s SDES %08lX\n", prog,
inet_ntoa(from.sin_addr), lw->ssrc);
}
while (cp < lp) {
switch ((*cp) & 0xFF) {
case RTCP_SDES_CNAME:
lw->cname = dupSdesItem(&cp);
break;
case RTCP_SDES_NAME:
lw->name = dupSdesItem(&cp);
break;
case RTCP_SDES_EMAIL:
lw->email = dupSdesItem(&cp);
break;
case RTCP_SDES_PHONE:
lw->phone = dupSdesItem(&cp);
break;
case RTCP_SDES_LOC:
lw->loc = dupSdesItem(&cp);
break;
case RTCP_SDES_TOOL:
lw->tool = dupSdesItem(&cp);
break;
case RTCP_SDES_PRIV:
{
char *zp = dupSdesItem(&cp);
if (zp != NULL) {
if (zp[0] == 1 &&
zp[1] == 'P') {
lw->port = atoi(zp + 2);
}
free(zp);
}
}
break;
case RTCP_SDES_END:
cp = lp;
break;
default:
{
char *zp = dupSdesItem(&cp);
if (zp != NULL) {
free(zp);
}
}
break;
}
}
if (debugging) {
dumpLwl(stderr, lw);
}
/* Search chain and see if a user with this
name is already know. If so, replace the
entry with this one. */
if (lw->cname != NULL) {
lr = conn;
llr = NULL;
while (lr != NULL) {
char *p = lw->cname, *q = lr->cname;
if (*p == '*') {
p++;
}
if (*q == '*') {
q++;
}
if (strcmp(p, q) == 0) {
lw->next = lr->next;
if (llr == NULL) {
conn = lw;
} else {
llr->next = lw;
}
destroyLwl(lr);
lw = NULL;
break;
}
llr = lr;
lr = lr->next;
}
/* If we didn't find an entry already in the
chain, link in the new entry. */
if (lw != NULL) {
lw->next = conn;
conn = lw;
if (verbose) {
logLwl(lw, "Connect: ");
}
changed();
}
if (!forward && lwl_nsites > 0) {
forwardLwlMessage(zp, rll, from.sin_addr);
}
} else {
/* Bogus item with no CNAME -- discard. */
if (debugging || verbose) {
fprintf(stderr, "Bogus SDES with no CNAME.\n");
if (!debugging) {
dumpLwl(stderr, lw);
}
}
destroyLwl(lw);
}
}
}
break;
/* BYE packet. This is sent when a listening host is
ceasing to listen in an orderly manner.
Identity here is by SSRC, since the
host is assumed to have properly announced
itself. Besides, that's the only ID in
the RTCP BYE packet, so it had darned
well be sufficient. */
case RTCP_BYE:
{
struct lwl *lr = conn, *llr = NULL;
if (prolix) {
printf("%s: %s BYE %08lX\n", prog,
inet_ntoa(from.sin_addr), rp->r.bye.src[0]);
}
while (lr != NULL) {
if (rp->r.bye.src[0] == lr->ssrc) {
if (llr == NULL) {
conn = lr->next;
} else {
llr->next = lr->next;
}
if (debugging) {
fprintf(stderr, "Releasing:\n");
dumpLwl(stderr, lr);
}
if (verbose) {
logLwl(lr, "Bye: ");
}
changed();
destroyLwl(lr);
break;
}
llr = lr;
lr = lr->next;
}
if (!forward && lwl_nsites > 0) {
forwardLwlMessage(zp, rll, from.sin_addr);
}
}
break;
/* Application request packets. The following application
extensions implement the various queries a client can make
regarding the current state of the listening host list. */
case RTCP_APP:
if (prolix) {
printf("%s: %s APP %.4s\n", prog,
inet_ntoa(from.sin_addr), p + 8);
}
/* SFlq -- Pattern match in cname and name and
return matches, up to the maximum
packet size. If either the query string
or the canonical name begins with an
asterisk, the asterisk(s) is(/are) ignored
and a precise match with the canonical
name is required. */
if (bcmp(p + 8, "SFlq", 4) == 0) {
struct lwl *lr = conn;
char b[1500];
rtcp_t *rp = (rtcp_t *) b;
char *ap, *pap;
int l, scandex = 0;
#ifdef RationalWorld
rp->common.version = RTP_VERSION;
rp->common.p = 0;
rp->common.count = 0;
rp->common.pt = RTCP_SDES;
#else
*((short *) rp) = htons((RTP_VERSION << 14) |
RTCP_SDES | (0 << 8));
#endif
ap = (char *) &(rp->r.sdes.src);
if (prolix) {
printf("%s: %s query \"%s\"\n", prog, inet_ntoa(from.sin_addr), p + 12);
}
lcase(p + 12);
while (lr != NULL) {
if (queryMatch(p + 12, lr)) {
char s[20];
pap = ap;
*((unsigned long *) ap) = lr->ssrc;
ap += sizeof(unsigned long);
addSDES(RTCP_SDES_CNAME, lr->cname +
(lr->cname[0] == '*' ? 1 : 0));
if (lr->name != NULL) {
addSDES(RTCP_SDES_NAME, lr->name);
}
if (lr->email != NULL) {
addSDES(RTCP_SDES_EMAIL, lr->email +
(lr->email[0] == '*' ? 1 : 0));
}
if (lr->phone != NULL) {
addSDES(RTCP_SDES_PHONE, lr->phone);
}
if (lr->loc != NULL) {
addSDES(RTCP_SDES_LOC, lr->loc);
}
if (lr->tool != NULL) {
addSDES(RTCP_SDES_TOOL, lr->tool);
}
sprintf(s, "\001P%d", lr->port);
addSDES(RTCP_SDES_PRIV, s);
{
struct sockaddr_in u;
u.sin_addr.s_addr = lr->naddr;
sprintf(s, "\001I%s", inet_ntoa(u.sin_addr));
addSDES(RTCP_SDES_PRIV, s);
}
sprintf(s, "\001T%lu", lr->ltime);
addSDES(RTCP_SDES_PRIV, s);
#ifdef NEEDED
/* If we're over the packet size limit,
let the user know there's more to be
retrieved starting at the given offset. */
if ((ap - b) > MaxReplyPacket) {
sprintf(s, "\001M%d", scandex);
addSDES(RTCP_SDES_PRIV, s);
}
#endif
*ap++ = RTCP_SDES_END;
/* Pad to next 32 bit boundary. */
while (((ap - b) & 3) != 0) {
*ap++ = RTCP_SDES_END;
}
if ((ap - b) > MaxReplyPacket) {
ap = pap;
break;
}
#ifdef RationalWorld
rp->common.count++;
#else
(((unsigned char *) rp)[0])++;
#endif
}
lr = lr->next;
scandex++;
}
l = ap - b;
rp->common.length = htons(((l + 3) / 4) - 1);
l = (ntohs(rp->common.length) + 1) * 4;
#ifdef HEXDUMP
if (hexdump) {
fprintf(stderr, "%s: %d bytes sent to socket.\n", prog, l);
xd(stderr, b, l, TRUE);
}
#endif
if (send(csock, b, l, 0) < 0) {
perror("sending query match reply");
}
}
/* SFms -- Retrieve the server's information
message, if any. If the server doesn't
publish a message, a null string is
returned in the reply packet. */
else if (bcmp(p + 8, "SFms", 4) == 0) {
#ifdef HEXDUMP
if (hexdump) {
fprintf(stderr, "%s: %d bytes sent to socket.\n",
prog, messagel);
xd(stderr, message, messagel, TRUE);
}
#endif
if (prolix) {
printf("%s: %s server message request\n", prog, inet_ntoa(from.sin_addr));
}
if (send(csock, message, messagel, 0) < 0) {
perror("sending server message");
}
}
break;
default:
if (debugging || verbose) {
fprintf(stderr, "Bogus payload type %d\n",
#ifdef RationalWorld
rp->common.pt
#else
((unsigned char *) rp)[1]
#endif
);
}
break;
}
}
close(csock);
}
#ifdef THREADS
/* Thread processing functions. */
#ifdef DBTHREAD
/* EDIT_THREAD -- Edit information about a service thread from
its packet in the ballOstring chain. */
static char *edit_thread(b)
struct ballOstring *b;
{
static char s[80];
sprintf(s, "%d %s %s", b->thread, inet_ntoa(b->site), ctime(&b->startTime));
s[strlen(s) - 1] = 0;
return s;
}
#endif
/* TIMEOUT_THREAD -- Handle timeout of sites that go away without
sending a "Bye" message. */
static void *timeout_thread(arg)
void *arg;
{
long ct;
struct ballOstring *b;
#ifdef DBTHREAD
fprintf(stderr, "Timeout thread running\n");
#endif
while (TRUE) {
sleep(120); /* Wait for next awake interval */
time(&ct);
#ifdef DBTHREAD
fprintf(stderr, "Timeout thread scanning connections\n");
#endif
Lock(bsLock);
b = bshead.next;
while (b != &bshead) {
#ifdef DBTHREAD
fprintf(stderr, " Thread %s\n", edit_thread(b));
#endif
if ((b->startTime + ServiceThreadTimeout) < ct) {
#ifdef DBTHREAD
fprintf(stderr, " Kill sent due to timeout.\n");
#endif
pthread_kill(b->thread, SIGUSR1);
}
b = b->next;
}
Unlock(bsLock);
release();
}
}
/* HTML_THREAD -- Update the HTML database(s) if something has
changed since the last time. */
static void *html_thread(arg)
void *arg;
{
#ifdef DBTHREAD
fprintf(stderr, "HTML update thread running\n");
#endif
while (TRUE) {
#ifdef DBTHREAD
fprintf(stderr, "HTML update thread examining connections\n");
#endif
updHTML();
/* In the threaded version, consider every 15 seconds as
equivalent to "immediate update on every change".
We could accomplish immediate update with a
semaphore signalling mechanism, but that would
cause the threaded and non-threaded versions to
diverge much more. Besides, nobody runs in immediate
update mode anyway, as far as I can determine. */
sleep((unsigned int) (htmlTime <= 0 ? 15 : htmlTime));
}
}
/* SERVICE_THREAD -- Thread to process user packet. One of
these is created every time a connection is
accepted. */
struct service_arg {
int sa_csock;
struct sockaddr_in sa_from;
struct ballOstring *sa_b;
};
static void killserv()
{
pthread_t us = pthread_self();
struct ballOstring *b;
#ifdef DBTHREAD
fprintf(stderr, "Gaaaak!!! Killserv received by thread %d.\n", us);
#endif
/* We're being killed by the timeout handler. Search for
our entry in the ballOstring chain. */
Lock(bsLock);
b = bshead.next;
while (b != &bshead) {
if (b->thread == us) {
/* Dechain from active thread list. */
b->prev->next = b->next;
b->next->prev = b->prev;
break;
}
b = b->next;
}
Unlock(bsLock);
if (b != &bshead) {
if (b->sa->sa_csock != -1) {
close(b->sa->sa_csock);
}
#ifdef DBTHREAD
fprintf(stderr, "Thread killed by timeout: %s\n", edit_thread(b));
#endif
free(b->sa);
free(b);
pthread_exit(NULL);
} else {
#ifdef DBTHREAD
fprintf(stderr, "Oops!!! Timeout kill request to thread %d, not in ballOstring.\n", us);
#endif
}
}
static void *service_thread(arg)
void *arg;
{
struct service_arg *sa = arg;
#ifdef DBTHREAD
fprintf(stderr, "Server thread %d for host %s launched\n", pthread_self(), inet_ntoa(sa->sa_from.sin_addr));
#endif
signal(SIGUSR1, killserv); /* Set signal to handle timeout */
sa->sa_b->thread = pthread_self();
servicePacket(sa->sa_csock, sa->sa_from);
#ifdef DBTHREAD
fprintf(stderr, "Server thread %d for host %s exiting\n", pthread_self(), inet_ntoa(sa->sa_from.sin_addr));
#endif
/* Dechain this thread's item from the ballOstring list. */
#ifdef DBTHREAD
fprintf(stderr, "Thread exiting: %s\n", edit_thread(sa->sa_b));
#endif
Lock(bsLock);
sa->sa_b->prev->next = sa->sa_b->next;
sa->sa_b->next->prev = sa->sa_b->prev;
free(sa->sa_b);
Unlock(bsLock);
free(sa); /* Release request packet */
pthread_exit(NULL);
}
#endif
/* Main program. */
main(argc, argv)
int argc;
char *argv[];
{
int i, length;
struct sockaddr_in name;
#ifdef THREADS
pthread_t timeout_tid, html_tid;
pthread_attr_init(&detached);
pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&detached, (size_t) 65536);
bshead.next = bshead.prev = &bshead;
#endif
/* Process command line options. */
prog = prog_name(argv[0]);
#ifdef PRUNEFACE
{ /* Interctive debugging of text pruning logic. */
char s[132], p[132];
char *zap;
while (TRUE) {
printf("--> ");
if (fgets(s, sizeof s, stdin) == NULL) {
break;
}
zap = p;
add_sdes_item(1, s, &zap);
*zap = 0;
printf( "\"%s\"\n", p + 2);
}
exit(0);
}
#endif
for (i = 1; i < argc; i++) {
char *op, opt;
op = argv[i];
if (*op == '-') {
opt = *(++op);
if (islower(opt)) {
opt = toupper(opt);
}
switch (opt) {
case 'D': /* -D -- Debug output to stderr */
debugging = TRUE;
break;
case 'F': /* -Fserv,... -- Forward to listed servers. */
forwardList(op + 1);
break;
case 'H': /* -Hname -- HTML file name base path */
htmlFile = op + 1;
break;
case 'I': /* -Isec -- Interval between HTML updates */
htmlTime = atoi(op + 1);
break;
case 'M': /* -Mfile -- Load server message from file */
{
FILE *fp = fopen(op + 1, "r");
long fl;
if (fp == NULL) {
fprintf(stderr, "%s: can't open server message file %s\n", prog, op + 1);
return 2;
}
fseek(fp, 0L, 2);
fl = ftell(fp);
rewind(fp);
messagel = ((int) fl) + 13;
message = (char *) malloc(messagel);
if (message != NULL) {
rtcp_t *rp = (rtcp_t *) message;
#ifdef RationalWorld
rp->common.version = RTP_VERSION;
rp->common.p = 0;
rp->common.count = 1;
rp->common.pt = RTCP_APP;
#else
*((short *) rp) = htons((RTP_VERSION << 14) |
RTCP_APP | (1 << 8));
#endif
rp->r.sdes.src = 0;
bcopy("SFmr", message + 8, 4);
fread(message + 12, (int) fl, 1, fp);
message[messagel - 1] = 0;
}
fclose(fp);
}
break;
case 'P': /* -Pport -- Port to listen on */
lwlport = atoi(op + 1);
break;
case 'R': /* -Rnsec -- Interval between HTML client-pull updates */
htmlRefresh = atoi(op + 1);
break;
case 'U': /* -U -- Print usage information */
case '?': /* -? -- Print usage information */
usage();
return 0;
case 'V': /* -V -- Show connects/disconnects */
verbose = TRUE;
if (op[1] == 'v' || op[1] == 'V') {
prolix = TRUE;
}
break;
#ifdef HEXDUMP
case 'X': /* -X -- Dump packets in hex */
hexdump = TRUE;
break;
#endif
case 'Z': /* -Zname -- HTML private file name base path */
htmlPrivateFile = op + 1;
break;
}
} else {
usage();
return 2;
}
}
/* If no server message has been loaded, create a void one. */
if (message == NULL) {
rtcp_t *rp;
messagel = 13;
message = (char *) malloc(messagel);
rp = (rtcp_t *) message;
#ifdef RationalWorld
rp->common.version = RTP_VERSION;
rp->common.p = 0;
rp->common.count = 1;
rp->common.pt = RTCP_APP;
#else
*((short *) rp) = htons((RTP_VERSION << 14) | RTCP_APP | (1 << 8));
#endif
rp->r.sdes.src = 0;
bcopy("SFmr", message + 8, 4);
message[messagel - 1] = 0;
}
/* Create the socket from which to read */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
return 1;
}
/* Create name with wildcards. */
name.sin_family = AF_INET;
name.sin_addr.s_addr = INADDR_ANY;
name.sin_port = htons(lwlport);
if (bind(sock, (struct sockaddr *) &name, sizeof name) < 0) {
perror("binding stream socket");
return 1;
}
signal(SIGHUP, exiting); /* Set signal to handle termination */
signal(SIGINT, exiting); /* Set signal to handle termination */
signal(SIGTERM, exiting); /* Set signal to handle termination */
signal(SIGPIPE, plumber); /* Catch "broken pipe" signals from disconnects */
if (listen(sock, 25) < 0) {
perror("calling listen for socket");
}
/* Find assigned port value and print it. */
length = sizeof(name);
if (getsockname(sock, (struct sockaddr *) &name, &length) < 0) {
perror("getting socket name");
return 1;
}
#ifdef SHOW_SOCKET
fprintf(stderr, "%s: socket port #%d\n", prog, ntohs(name.sin_port));
#endif
#ifdef THREADS
pthread_create(&timeout_tid, &detached, timeout_thread, NULL);
if (htmlFile != NULL || htmlPrivateFile != NULL) {
pthread_create(&html_tid, &detached, html_thread, NULL);
}
#else
signal(SIGALRM, release); /* Set signal to handle timeout */
alarm(TockTock); /* Set alarm clock to purge idle hosts */
#endif
changed(); /* Create initial HTML file */
setlinebuf(stdout); /* Set stdout to line buffering for log */
/* Process requests from the socket. */
while (TRUE) {
int csock;
struct sockaddr_in from; /* Sending host address */
int fromlen; /* Length of sending host address */
#ifdef THREADS
struct service_arg *sap;
pthread_t service_tid;
#endif
errno = 0;
do {
fromlen = sizeof from;
csock = accept(sock, (struct sockaddr *) &from, &fromlen);
#ifdef DBTHREAD
fprintf(stderr, "Accept %d\n", csock);
#endif
if (csock >= 0) {
break;
}
} while (errno == EINTR);
if (csock < 0) {
perror("accepting connection to socket");
continue;
}
if (prolix) {
printf("%s: %s accept\n", prog, inet_ntoa(from.sin_addr));
}
#ifdef THREADS
sap = (struct service_arg *) malloc(sizeof(struct service_arg));
if (sap != NULL) {
struct ballOstring *b;
sap->sa_csock = csock;
sap->sa_from = from;
b = (struct ballOstring *) malloc(sizeof(struct ballOstring));
if (b != NULL) {
sap->sa_b = b;
b->sa = sap;
Lock(bsLock);
b->next = bshead.next;
b->prev = &bshead;
bshead.next = b;
b->next->prev = b;
b->thread = -1;
time(&(b->startTime));
b->site = from.sin_addr;
Unlock(bsLock);
if (pthread_create(&service_tid, &detached, service_thread, sap) != 0) {
fprintf(stderr, "%s: Cannot launch server thread for request from %s: %s",
prog, inet_ntoa(from.sin_addr), strerror(errno));
close(csock);
}
} else {
free(sap);
fprintf(stderr, "Unable to allocate memory for ballOstring packet.\n");
close(csock);
}
} else {
fprintf(stderr, "Unable to allocate memory for service_arg packet.\n");
close(csock);
}
#else
servicePacket(csock, from);
#endif
}
#ifdef MEANS_OF_EXIT
close(sock);
return 0;
#endif
}